package submarines;

import submarines.notices.*;
import java.io.Console;
import java.util.LinkedList;
import java.util.List;
import SubmarinesBindings.*;
import javax.xml.bind.*;

/**
 *
 * @author elad
 */
abstract public  class Player {
    protected String name;
    private int score;
    protected String pType;
    public ownBoard myBoard;
    protected LinkedList<enemyBoard> competitorBoard;
    LinkedList<Notice> notices;
    public int deathCause=0;

    /*
     * Constructor
     */
    public Player(String userName, int userScore)
    {
        this.name=userName;
        this.score=userScore;
        this.myBoard= new ownBoard(this);
        notices = new LinkedList<Notice>();
        competitorBoard = new LinkedList<enemyBoard>();
    }
    
    public Player(String name, List<SubmarinesBindings.Boat> xmlBoats, SubmarinesBindings.Board b) throws JAXBException {
        this.name = name;
        this.myBoard = new ownBoard(b, xmlBoats, this);
        notices = new LinkedList<Notice>();
        competitorBoard = new LinkedList<enemyBoard>();
    }
    
    protected void setType(String type) {
        pType = type;
    }

    public String getType() {
        return pType;
    }
    
    public LinkedList<Notice> getNotices()
    {
        return this.notices;
    }
    
    public LinkedList<Notice> getNotices(boolean empty)
    {
        if (!empty)
            return this.notices;
        else {
            LinkedList<Notice> tmp = new LinkedList<Notice>(notices);
            notices.clear();
            return tmp;
        }
    }

    void reInitiate() {
        enemyBoard eBoard;
        int i;
        myBoard.cleanBoard();
        myBoard.subsLeft = 0;
        fillBoard();

        while (notices.size() > 0)
            notices.remove();
        while (competitorBoard.size() > 0)
            competitorBoard.remove();
    }
    
    public Player(String userName)
    {
        this.name=userName;
        this.score=0;
    }
    
    public void addCompetitor(Player enemy) {
        enemyBoard b = new enemyBoard(enemy);
        competitorBoard.add(b);
    }
    
    public void removeCompetitor(Player enemy) {
       int size = competitorBoard.size();
        boolean removed = false;
        int i = 0;
        while (removed==false)
        {
            enemyBoard board = competitorBoard.get(i);
            if(board.belongsTo(enemy))
            {   
                competitorBoard.remove(i);
                removed = true;
            }
            i++;
        }
    }
        
    
    public Board getCompetitorBoard(Player enemy)
    {
        int size = competitorBoard.size();
        for (int i=0; i<size; i++)
        {
            enemyBoard board = competitorBoard.get(i);
            if(board.belongsTo(enemy))
            {
                return board;
            }
        }
        return null;
    }
    
    public String getName()
    {
        return this.name;
    }
    
    public void setName(String userName)
    {
        this.name = userName;
    }
    
    public int getScore()
    {
        return this.score;
    }
    
    public void setScore()
    {
        this.score++;
    }
    
    public void increaseScore()
    {
        this.score++;
    }
    
    public void addNotice(Notice n) {
        notices.add(n);
    }
    
    void readNotices() {
        int size = notices.size(); 
        while (size != 0)
        {
            Notice currNotice = notices.get(0);
            switch (currNotice.getNoticeType())
            {
                case PLAYER_GOT_HIT:
                    notifyPlayerGotHit(currNotice);
                    break;
                
                case PLAYER_SUBMARINE_SUNK:
                    notifyPlayerSubmarineSunk(currNotice);
                    break;
                
                case PLAYER_QUIT:
                    notifyPlayerQuit(currNotice);
                    break;
                case PLAYER_GOT_LUCKY:
                    notifyPlayerGotLucky(currNotice);
                    break;
            }
            notices.remove(0);
            size = notices.size(); 
        }
    }

     public void notifyPlayerGotHit(Notice currNotice)
    {
        NoticePlayerGotHit gotHit = (NoticePlayerGotHit)currNotice;
        Player relevantPlayer = gotHit.getPlayer();
        Board playerBoard = this.getCompetitorBoard(relevantPlayer);
        int Row = gotHit.getRow();
        int Col = gotHit.getCol();
        
        playerBoard.markSpot(Row, Col, Config.HIT_SQUARE_SIGN);
    }
    
    public void notifyPlayerSubmarineSunk(Notice currNotice)
    {
        NoticePlayerSubmarineSunk subSunk = (NoticePlayerSubmarineSunk)currNotice;
        Player relevantPlayer = subSunk.getPlayer();
        int size = subSunk.getSize();
        enemyBoard enemy = relevantPlayer.getEnemyBoardByName(relevantPlayer);
        enemy.removeSubFromSubs(size);
    }
    
    public void notifyPlayerGotLucky(Notice currNotice) {
        NoticePlayerGotLucky gotHit = (NoticePlayerGotLucky)currNotice;
        Player relevantPlayer = gotHit.getPlayer();
        Board playerBoard = this.getCompetitorBoard(relevantPlayer);
        int Row = gotHit.getRow();
        int Col = gotHit.getCol();
        
        playerBoard.markSpot(Row, Col, Config.MISSED_SQUARE_SIGN);        
    }
            
    public void notifyPlayerQuit(Notice currNotice)
    {
        NoticePlayerQuit quit = (NoticePlayerQuit)currNotice;
        Player relevantPlayer = quit.getPlayer();
        this.removeCompetitor(relevantPlayer);
    }
    
   
    abstract public void fillBoard();
    
    abstract public boolean playTurn();
    
    public boolean isDead() {
        return !myBoard.anySubsLeft();
    }
    
 //abstract TurnManager.turnAction getTurnAction();
    
     public void cleanBoard()
     {
         myBoard.cleanBoard();
     }
     
     //Returns: -1: no hit
     // 0 : hit
     // 0> size of sunken ship
     public int getTorpedo(int row, int Col)
     {
         return myBoard.isHit(row, Col);
     }
     
     public enemyBoard getEnemyBoardByName(Player enemy) {
         enemyBoard e = null;
         int i;
         for (i = 0; i < competitorBoard.size(); i++) {
             e = competitorBoard.get(i);
             if (e.belongsTo(enemy)) {
                 return e;
             }
         }
         return e;
     }
     
     public boolean markOnBoard(char ch, String target, int row, int col) {
         enemyBoard eBoard = null;
         Player p;
         int i;
         
         p = Main.pManager.getPlayerByName(target);
         if (p == null)
             return false;
         for (i = 0; i < competitorBoard.size(); i++) {
             eBoard = competitorBoard.get(i);
             if (eBoard.belongsTo(p))
                 break;
         }
         if (eBoard == null)
             return false;

         char sign = eBoard.getSquare(row, col).getSign();
         
         if ((ch == Config.EMPTY_SQUARE_SIGN) && (sign != Config.MARKED_SQUARE_SIGN))
             return false;
         if ((ch == Config.MARKED_SQUARE_SIGN) && (sign != Config.EMPTY_SQUARE_SIGN))
             return false;
         if ((ch == Config.HIT_SQUARE_SIGN) && 
                 ((sign != Config.MARKED_SQUARE_SIGN) && (sign != Config.EMPTY_SQUARE_SIGN)))
             return false;
         eBoard.markSpot(row, col, ch);
         return true;
     }
     
     public boolean markOnBoard(PlayerManager pManager, char ch, String target, int row, int col) { //for web game
         enemyBoard eBoard = null;
         Player p;
         int i;
         
         p = pManager.getPlayerByName(target);
         if (p == null)
             return false;
         for (i = 0; i < competitorBoard.size(); i++) {
             eBoard = competitorBoard.get(i);
             if (eBoard.belongsTo(p))
                 break;
         }
         if (eBoard == null)
             return false;

         char sign = eBoard.getSquare(row, col).getSign();
         
         if ((ch == Config.EMPTY_SQUARE_SIGN) && (sign != Config.MARKED_SQUARE_SIGN))
             return false;
         if ((ch == Config.MARKED_SQUARE_SIGN) && (sign != Config.EMPTY_SQUARE_SIGN))
             return false;
         if ((ch == Config.HIT_SQUARE_SIGN) && 
                 ((sign != Config.MARKED_SQUARE_SIGN) && (sign != Config.EMPTY_SQUARE_SIGN)))
             return false;
         eBoard.markSpot(row, col, ch);
         return true;
     }
     
     public boolean sendTorpedo(PlayerManager pManager, NotficationManager nManager, String currPlayerName, String enemyPlayerName, int row, int col) //for web
     {
        int hitResult = 0;
                 
        submarines.Player currPlayer =  pManager.getPlayerByName(currPlayerName);
        submarines.Player enemyPlayer =  pManager.getPlayerByName(enemyPlayerName);
        
        enemyBoard eBoard = currPlayer.getEnemyBoardByName(enemyPlayer);
        if (eBoard.canBeTargeted(row, col)) {
            hitResult = enemyPlayer.getTorpedo(row, col);
            if (hitResult >= 0) {
                //mark hit on the target board
                //Main.userInterface.printAlert("You've hit " + enemyPlayerName);
                markOnBoard(pManager, Config.HIT_SQUARE_SIGN, enemyPlayerName, row, col);
                NoticePlayerGotHit n = new NoticePlayerGotHit(enemyPlayer, Notice.NoticeType.PLAYER_GOT_HIT, row, col);
                //nManager.spreadTheWord(this, enemyPlayer, n);

                if (hitResult > 0) 
                {    
                    NoticePlayerSubmarineSunk sunk = new NoticePlayerSubmarineSunk(enemyPlayer, Notice.NoticeType.PLAYER_SUBMARINE_SUNK, hitResult);
                    //nManager.spreadTheWord(this, enemyPlayer, sunk);
                }
                  
                if (enemyPlayer.isDead()) 
                {
                    NoticePlayerQuit q = new NoticePlayerQuit(enemyPlayer, Notice.NoticeType.PLAYER_QUIT);
                    pManager.playerQuit(enemyPlayer);
                    //nManager.spreadTheWord(this, this, q);
                    //if (Main.pManager.numPlayersLeft() == 1) {
                     //   played = true;
                    //}
                }
            } 
            else 
            {
                markOnBoard(pManager, Config.MISSED_SQUARE_SIGN, enemyPlayerName, row, col);
                //Main.userInterface.printAlert("You've missed");
                NoticePlayerGotLucky n = new NoticePlayerGotLucky(enemyPlayer, Notice.NoticeType.PLAYER_GOT_LUCKY, row, col);
                //nManager.spreadTheWord(this,enemyPlayer, n);
                //        played = true;
            }
            //e = getEnemyBoardByName(t);
        } else {
            //Main.userInterface.printAlert("You cannot attack  " + enemyPlayer.getName() + " on coordinates " + (char)('A' + col + Config.BOARD_START) + "" + ((row + Config.BOARD_START+1)));
            return false;
        }
        return true;
    }
     
     public boolean sendTorpedo(String currPlayerName, String enemyPlayerName, int row, int col)
     {
        int hitResult = 0;
                 
        submarines.Player currPlayer =  Main.pManager.getPlayerByName(currPlayerName);
        submarines.Player enemyPlayer =  Main.pManager.getPlayerByName(enemyPlayerName);
        
        enemyBoard eBoard = currPlayer.getEnemyBoardByName(enemyPlayer);
        if (eBoard.canBeTargeted(row, col)) {
            hitResult = enemyPlayer.getTorpedo(row, col);
            if (hitResult >= 0) {
                //mark hit on the target board
                Main.userInterface.printAlert("You've hit " + enemyPlayerName);
                markOnBoard(Config.HIT_SQUARE_SIGN, enemyPlayerName, row, col);
                NoticePlayerGotHit n = new NoticePlayerGotHit(enemyPlayer, Notice.NoticeType.PLAYER_GOT_HIT, row, col);
                Main.nManager.spreadTheWord(this, enemyPlayer, n);

                if (hitResult > 0) 
                {    
                    NoticePlayerSubmarineSunk sunk = new NoticePlayerSubmarineSunk(enemyPlayer, Notice.NoticeType.PLAYER_SUBMARINE_SUNK, hitResult);
                    Main.nManager.spreadTheWord(this, enemyPlayer, sunk);
                }
                  
                if (enemyPlayer.isDead()) 
                {
                    NoticePlayerQuit q = new NoticePlayerQuit(enemyPlayer, Notice.NoticeType.PLAYER_QUIT);
                    Main.pManager.playerQuit(enemyPlayer);
                    Main.nManager.spreadTheWord(this, this, q);
                    //if (Main.pManager.numPlayersLeft() == 1) {
                     //   played = true;
                    //}
                }
            } 
            else 
            {
                markOnBoard(Config.MISSED_SQUARE_SIGN, enemyPlayerName, row, col);
                Main.userInterface.printAlert("You've missed");
                NoticePlayerGotLucky n = new NoticePlayerGotLucky(enemyPlayer, Notice.NoticeType.PLAYER_GOT_LUCKY, row, col);
                Main.nManager.spreadTheWord(this,enemyPlayer, n);
                //        played = true;
            }
            //e = getEnemyBoardByName(t);
        } else {
            Main.userInterface.printAlert("You cannot attack  " + enemyPlayer.getName() + " on coordinates " + (char)('A' + col + Config.BOARD_START) + "" + ((row + Config.BOARD_START+1)));
            return false;
        }
        return true;
    }
     
     public boolean playerQuitGame()
     {
          NoticePlayerQuit q = new NoticePlayerQuit(this, Notice.NoticeType.PLAYER_QUIT);
          Main.pManager.playerQuit(this);
          Main.nManager.spreadTheWord(this, this, q);
          return true;
     }
     
      public boolean playerQuitGame(PlayerManager pManager, NotficationManager nManager)
     {
          NoticePlayerQuit q = new NoticePlayerQuit(this, Notice.NoticeType.PLAYER_QUIT);
          pManager.playerQuit(this);
          //nManager.spreadTheWord(this, this, q);
          return true;
     }
}